﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Gma.UserActivityMonitor;

namespace ChartEdit
{
    public partial class EditForm : Form
    {

        private const int WM_SETREDRAW      = 0x000B;
        private const int WM_USER           = 0x400;
        private const int EM_GETEVENTMASK   = (WM_USER + 59);
        private const int EM_SETEVENTMASK   = (WM_USER + 69);


        [DllImport("user32", CharSet = CharSet.Auto)]
        private extern static IntPtr SendMessage(IntPtr hWnd, int msg, int wParam, IntPtr lParam);
         
        IntPtr eventMask = IntPtr.Zero;
        public IntPtr MyHandle;



        private MainForm parentForm;

        public Editor ed;
        public Chart chart;
        public Timer ScrollTimer;
        public KeyManager KM = new KeyManager();

        private Graphics g;
        private Bitmap backBuffer;
        //private Bitmap BackBuffer;

        private bool scrollFlag = true;
        public volatile int VScrollChartValue;
        public volatile bool RendererBusy = false;

        private System.Diagnostics.Stopwatch sWatch;
        private System.Diagnostics.Stopwatch rWatch;

        private System.Threading.Timer rTimer;
        public int RDelay=10;
        

        public EditForm( string fileName, MainForm parent )
        {
            parentForm = parent;
            InitializeComponent();
            MyHandle = Handle;
            // HOW TO HOOK SOME FUCKING KEYS, MAN
            //HookManager.KeyPress += new KeyPressEventHandler( HookManager_KeyPress ); // HOLY SHIT FIND THESE FUNCTIONS
            HookManager.KeyDown += new KeyEventHandler( HookManager_KeyDown );
            HookManager.KeyUp += new KeyEventHandler(HookManager_KeyUp);
            //HookManager.MouseWheel += new MouseEventHandler(HookManager_MouseWheel);

            chart = new Chart();
            chart.Load( fileName );

            this.Text = chart.Song[ "Name" ].Value;

            #region Treeview Stuff
            RootTreeNode trackRoot = new RootTreeNode( "Tracks" );
            trackRoot.RootChart = chart;

            RootTreeNode sectionRoot = new RootTreeNode( "Sections" );
            sectionRoot.RootChart = chart;

            foreach ( NoteTrack noteTrack in chart.NoteTracks )
            {
                InstrumentTreeNode dtn = trackRoot.FindOrCreateInstrument( noteTrack.Instrument );

                TrackTreeNode ttn = new TrackTreeNode();


                ttn.Track = noteTrack;
                ttn.Text = noteTrack.Difficulty;
                ttn.RootChart = chart;

                dtn.Nodes.Add( ttn );

                dtn.Collapse();
            }

            foreach ( EventsSectionEntry eventEntry in chart.Events )
            {
                if ( eventEntry.Type != EventType.Text )
                    continue;

                if ( eventEntry.TextValue.IndexOf( "section ", StringComparison.OrdinalIgnoreCase ) != 0 )
                    continue;

                SectionTreeNode stn = new SectionTreeNode();

                stn.RootChart = chart;
                stn.EventSection = eventEntry;
                stn.Text = eventEntry.TextValue.Substring( 8 );

                sectionRoot.Nodes.Add( stn );
            }
            sectionRoot.Collapse();
            treeViewTracks.Nodes.Add( trackRoot );
            treeViewTracks.Nodes.Add( sectionRoot );
            // treeViewSections.Nodes.Add( sectionRoot );

            treeViewTracks.ExpandAll();
            //treeViewSections.ExpandAll(); 
            #endregion

            this.SetStyle( ControlStyles.ResizeRedraw, true );
            this.SetStyle( ControlStyles.UserPaint, true );

            ScrollTimer = new Timer();
            ScrollTimer.Interval = 50;
            ScrollTimer.Tick += new System.EventHandler( this.scrollTimerTick );
            ScrollTimer.Start();

            ed = new Editor( pictureChart.Width, pictureChart.Height, chart, this );
            sWatch = new System.Diagnostics.Stopwatch();
            rWatch = new System.Diagnostics.Stopwatch();

            backBuffer = new Bitmap(pictureChart.Width,pictureChart.Height);
            g = Graphics.FromImage((Image)backBuffer);
            pictureChart.Image = (Image)backBuffer;
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed;
            g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low;
            g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
            g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
            //pictureChart.Image = ed.R.img;

            vScrollChart.Maximum = Math.Max( ( int )ed.song.Length(),
                ((int)(ed.OT.GetTime(ed.chart.LastIndex) * 1000f)));
            vScrollChart.Minimum = 0;
            vScrollChart.LargeChange = 1000;
            vScrollChart.SmallChange = 10;
            vScrollChart.Value = vScrollChart.Maximum;
            VScrollChartValue = vScrollChart.Value;
            ed.COffset = 0f;

            System.Threading.TimerCallback rDelegate = new System.Threading.TimerCallback( this.threadedRender);
            rTimer = new System.Threading.Timer(rDelegate, null, 0, RDelay);
        }

        public void Save()
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.DefaultExt = ".chart";
            sfd.Filter = "Charts (*.chart)|*.chart|All Files (*.*)|*.*";
            sfd.Title = "Save Chart";
            sfd.FileName = chart.Song["Name"].Value;
            if ( sfd.ShowDialog() != DialogResult.OK )
                return;

            chart.Save( sfd.FileName );
        }

        bool ChildrenFocused( Control ctrl )
        {
            if ( ctrl == null )
                return false;

            foreach ( Control subCtrl in ctrl.Controls )
            {
                if ( ChildrenFocused( subCtrl ) )
                    return true;
            }

            return ctrl.Focused;
        }

        void HookManager_MouseWheel(object sender, MouseEventArgs e)
        {
            if (!ChildrenFocused(this))
                return;
            ed.Shift(ed.Quantization * e.Delta/120);
        }

        void HookManager_KeyDown( object sender, KeyEventArgs e )
        {
            
            if ( !ChildrenFocused( this ) )
                return;
            e.SuppressKeyPress = true;
            KM.Press((int)e.KeyCode);
            ed.PressKeys(KM.KeysPressed(false),KM.KeysPressed(true));

            String strTest = "";
            foreach (int i in KM.KbKeys)
            {
                strTest += i.ToString() + ",";
            }
            foreach (KMKey k in KM.Keys)
            {
                if (k.Type == KeyType.QuantizeUp && (int)e.KeyCode == k.Key)
                    parentForm.toolStripQBox.SelectedIndex++;
                if (k.Type == KeyType.QuantizeDown && (int)e.KeyCode == k.Key)
                {
                    
                    if(parentForm.toolStripQBox.SelectedIndex>0)
                        parentForm.toolStripQBox.SelectedIndex--;
                }
            }
            this.Text = strTest;
        }

        void HookManager_KeyUp(object sender, KeyEventArgs e)
        {

            if (!ChildrenFocused(this))
                return;
            e.SuppressKeyPress = true;

            KM.Release((int)e.KeyCode);
            ed.PressKeys(KM.KeysPressed(false), KM.KeysPressed(true));

            String strTest = "";
            foreach (int i in KM.KbKeys)
            {
                strTest += i.ToString() + ",";
            }
            this.Text = strTest;
        }

        private void hScrollBar_Scroll( object sender, ScrollEventArgs e )
        {
        }

        private void threadedRender(object sender)
        {

            rWatch.Reset();
            rWatch.Start();
            if (!RendererBusy)
            {
                RendererBusy = true;

                lock (ed)
                {
                    ed.UpdateTrackPosition();
                }
                Render(MyHandle);

                //VScrollChartValue = (int)(ed.COffset * 1000f);
                try
                {
                    rTimer.Change(0, RDelay);
                }
                catch (System.ObjectDisposedException) { }
                
                RendererBusy = false;
            }
        }


        private void scrollTimerTick( object sender, EventArgs e )
        {
            VScrollChartValue = Math.Min(Math.Max(VScrollChartValue, 0),vScrollChart.Maximum);
            scrollFlag = false;
            vScrollChart.Value = vScrollChart.Maximum - VScrollChartValue;
            scrollFlag = true;
        }
        
        public void Render(IntPtr handle)
        {
            try
            {
                // Stop redrawing:
                SendMessage(handle, WM_SETREDRAW, 0, IntPtr.Zero);
                // Stop sending of events:
                eventMask = SendMessage(handle, EM_GETEVENTMASK, 0, IntPtr.Zero);

                // change colors and stuff in the RichTextBox




                ed.SecPerH = ((float)vScrollHeightScale.Value / vScrollHeightScale.Maximum) * 7.5f;
                ed.Think();

                try
                {
                    lock (ed)
                    {
                        g.DrawImageUnscaled((Image)ed.R.img, 0, 0);
                        
                        //pictureChart.Refresh();
                    }
                }
                catch (System.ArgumentException)
                {
                }
            }
            finally
            {
                // turn on events
                SendMessage(handle, EM_SETEVENTMASK, 0, eventMask);
                // turn on redrawing
                SendMessage(handle, WM_SETREDRAW, 1, IntPtr.Zero);
                pictureChart.Invalidate();
            }
        }


        protected override void OnPaint( PaintEventArgs e )
        {
            base.OnPaint( e );

            //Render();

            /*
            ed.Render();
            pictureChart.Update();
            pictureChart.Image = ed.R.img;

            pictureChart.Update();*/
        }

        private void vScrollChart_ValueChanged( object sender, EventArgs e )
        {
            if (scrollFlag)
            {
                VScrollChartValue = vScrollChart.Maximum - vScrollChart.Value;
                ed.COffset = (vScrollChart.Maximum - vScrollChart.Value) / 1000f;
                ed.song.SetPosition((uint)(ed.COffset * 1000));
                //Render();
            }
        }

        private void vScrollHeightScale_ValueChanged( object sender, EventArgs e )
        {
            float derp = ((float)vScrollHeightScale.Value / vScrollHeightScale.Maximum) * 7.5f;
            derp = (float)Math.Sqrt((double)derp)*2+0.1f;
            //ed.SecPerH = ( ( float )vScrollHeightScale.Value / vScrollHeightScale.Maximum ) * 7.5f;
            lock (ed)
            {
                ed.SecPerH = derp;
            }
            //Render();
        }

        private void treeView1_AfterSelect( object sender, TreeViewEventArgs e )
        {
            if ( e.Node is TrackTreeNode )
            {
                TrackTreeNode ttn = e.Node as TrackTreeNode;

                lock (ed)
                {
                    ed.LoadTrack(ttn.Track.Name);
                }
                //Render();
            }

        }

        private void treeView1_DoubleClick( object sender, EventArgs e )
        {
            if ( treeViewTracks.SelectedNode == null )
                return;

            if ( treeViewTracks.SelectedNode is SectionTreeNode )
            {
                SectionTreeNode sectTreeNode = treeViewTracks.SelectedNode as SectionTreeNode;

                string eSString = "section " + sectTreeNode.EventSection.TextValue;
                EventsSectionEntry n = chart.Events[ sectTreeNode.EventSection.TextValue ];
                float percentThing = ( 1f / 1.015f ) - ( 1f / 1.03f ) * ( ed.OT.GetTime( n.Offset - chart.HalfResolution ) / ed.OT.songLength );
                vScrollChart.Value = vScrollChart.Maximum - (int)(ed.OT.GetTime(n.Offset)*1000);
                //MessageBox.Show( "JUMPING TO: " + sectTreeNode.EventSection.TextValue );
            }
        }

        private void treeViewTracks_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                if (treeViewTracks.SelectedNode is RootTreeNode)
                {
                    switch (treeViewTracks.SelectedNode.Text)
                    {
                        case("Tracks"):
                            break;
                        case("Sections"):
                            break;
                    }
                }
            }
        }

        private void treeViewSections_AfterSelect( object sender, TreeViewEventArgs e )
        {

        }

        private void pictureChart_SizeChanged( object sender, EventArgs e )
        {
            ed.SecPerH = ( ( float )vScrollHeightScale.Value / vScrollHeightScale.Maximum ) * 7.5f;
            ed.Resize( pictureChart.Width, pictureChart.Height );

            backBuffer = new Bitmap(pictureChart.Width, pictureChart.Height);
            g = Graphics.FromImage((Image)backBuffer);
            pictureChart.Image = (Image)backBuffer;
        }

        private void pictureChart_Click( object sender, EventArgs e )
        {
            /*
            Note debugNote=new Note();
            debugNote.ArmoredNote=false;
            debugNote.EventName="";
            debugNote.FlipsHOPO=false;
            debugNote.ForcesHOPO=false;
            debugNote.ForcesStrum=false;
            debugNote.Fret=0;
            debugNote.IsANote=true;
            debugNote.IsChord=false;
            debugNote.IsHopo=false;
            debugNote.IsInSlide=false;
            debugNote.IsInSP=false;
            debugNote.Length=0;
            debugNote.Offset=0;
            debugNote.PointValue=50;
            debugNote.SpecialFlag=0;
            debugNote.Type=NoteType.Regular;
            chart.HerpDerp(debugNote, ed.noteTrack.Name);
            */
        }

        private void pictureChart_MouseDown( object sender, MouseEventArgs e )
        {
            ed.MouseDown = true;
            /*lock (ed)
            {
                ed.MouseDown(e.X, e.Y, (int)e.Button, e.Delta);
            }*/
        }

        private void pictureChart_MouseMove(object sender, MouseEventArgs e)
        {
            ed.MouseMove = true;
            ed.MouseX = e.X;
            ed.MouseY = e.Y;
            /*
            lock (ed)
            {
                ed.MouseMove(e.X, e.Y);
            }*/
        }

        private void pictureChart_MouseUp(object sender, MouseEventArgs e)
        {
            ed.MouseUp = true;
            //ed.MouseUp(e.X, e.Y);
        }

        private void EditForm_Shown( object sender, EventArgs e )
        {
            //ed.SecPerH = ((float)vScrollHeightScale.Value / vScrollHeightScale.Maximum) * 7.5f;
            //Render();
        }

        private void buttonPlay_Click( object sender, EventArgs e )
        {
            ed.clapper.Start();
            ed.PlaySong();
            
        }

        private void buttonPause_Click( object sender, EventArgs e )
        {
            ed.StopSong();
            //ScrollTimer.Stop();
        }

        private void trackSpeed_Scroll( object sender, EventArgs e )
        {
            float value = trackSpeed.Value;
            float max = trackSpeed.Maximum;

            float scale = value / max;

            ed.song.SetSpeed( scale );
        }

        private void EditForm_FormClosing( object sender, FormClosingEventArgs e )
        {
            rTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            rTimer.Dispose();
            ed.song.Stop();
            ScrollTimer.Stop();
            g.Dispose();
        }

    }
}